package net.lindeer.lang

class AstPrinter : Expr.Visitor<String> {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val expression = Expr.Binary(
                    Expr.Unary(
                            Token(Token.Type.MINUS, "-", null, 1),
                            Expr.Literal(123)),
                    Token(Token.Type.STAR, "*", null, 1),
                    Expr.Grouping(Expr.Literal(92.23))
            )
            println(AstPrinter().print(expression))
        }
    }

    fun print(expr: Expr): String {
        return expr.accept(this)
    }

    override fun visitBinaryExpr(expr: Expr.Binary): String {
        return parenthesize(expr.operator.lexeme, expr.left, expr.right)
    }

    override fun visitGroupingExpr(expr: Expr.Grouping): String {
        return parenthesize("group", expr.expression)
    }

    override fun visitLiteralExpr(expr: Expr.Literal): String {
        return expr.value.toString()
    }

    override fun visitUnaryExpr(expr: Expr.Unary): String {
        return parenthesize(expr.operator.lexeme, expr.right)
    }

    private fun parenthesize(name: String, vararg exprs: Expr): String {
        val str = exprs.toList().joinToString(" ") { it.accept(this@AstPrinter) }
        return "($name $str)"
    }

    override fun visitVariableExpr(expr: Expr.Variable): String {
        return expr.name.lexeme
    }

    override fun visitAssignExpr(expr: Expr.Assign): String {
        return "${expr.name.lexeme}=${expr.value}"
    }

    override fun visitLogicalExpr(expr: Expr.Logical): String {
        return "${expr.left.accept(this)}${expr.operator.lexeme}${expr.right.accept(this)}"
    }

    override fun visitCallExpr(expr: Expr.Call): String {
        return "<Fn ${expr.callee}>"
    }

    override fun visitGetterExpr(expr: Expr.Getter): String {
        return "${expr.who}.${expr.name.lexeme}"
    }

    override fun visitSetterExpr(expr: Expr.Setter): String {
        return "${expr.who}.${expr.name.lexeme}=${expr.right}"
    }

    override fun visitThisExpr(expr: Expr.This): String {
        return "${expr.keyword}=$expr"
    }

    override fun visitSuperExpr(expr: Expr.Super): String {
        return "${expr.keyword}=$expr"
    }
}
